package com.jsn.android.audio2

import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.AudioTrack
import android.media.MediaRecorder
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.os.postDelayed
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.observe
import com.jsn.android.audio2.sample.CameraActivity
import com.jsn.android.audio2.sample.RoomActivity
import com.jsn.baselibx.utils.JumpUtil
import com.jsn.baselibx.utils.Logs
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.concurrent.atomic.AtomicBoolean


fun Context.toast(str: String) = Toast.makeText(this, str, Toast.LENGTH_SHORT).show()

class AudioRecordActivity : AppCompatActivity() {

    val job = SupervisorJob()

    val jobScope = CoroutineScope(Dispatchers.IO + job)

    var thread: Thread? = null

    var audioRecord: AudioRecord? = null

    val source = MediaRecorder.AudioSource.MIC
    val sampleRate = 44100
    val channel = AudioFormat.CHANNEL_IN_MONO
    val audioFormat = AudioFormat.ENCODING_PCM_16BIT

    var file: File? = null
    lateinit var file1: File

    var fileOutputStream: FileOutputStream? = null

    var size = 0

    val flowing = MutableLiveData<Boolean>().apply { value = false }

    //memory visibility
    @Volatile
    var recordStart = false

    val REQUEST_RECORD = 1

    var loopStopped: AtomicBoolean = AtomicBoolean(true)


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        go()

        bt_to_wav.setSafeListener {
            JumpUtil.overlay(this,WAVActivity::class.java)
        }
        bt_to_mp4.setSafeListener {
            JumpUtil.overlay(this,Mp4Activity::class.java)
        }

        file1 = this.run {
            val mainActivity = this
            Log.e("fuckfile", mainActivity.externalCacheDir!!.absolutePath)
            Log.e("fuckfile", mainActivity.cacheDir!!.absolutePath)
            Log.e("fuckfile", mainActivity.filesDir!!.absolutePath)
            mainActivity.cacheDir!!
        }

        val minBufferSize: Int =
            AudioRecord.getMinBufferSize(sampleRate, channel, audioFormat).also { size = it }
        if (minBufferSize == AudioRecord.ERROR_BAD_VALUE) return
        audioRecord = AudioRecord(source, sampleRate, channel, audioFormat, minBufferSize)
        start.setOnClickListener {
            val sdk = Build.VERSION.SDK_INT
            val checkSelfPermission =
                ContextCompat.checkSelfPermission(this, android.Manifest.permission.RECORD_AUDIO)
            val checkSelfPermission1 = if (sdk <= 28)
                (ContextCompat.checkSelfPermission(
                    this,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE
                )) else PackageManager.PERMISSION_GRANTED
            if (checkSelfPermission == PackageManager.PERMISSION_GRANTED && checkSelfPermission1 == PackageManager.PERMISSION_GRANTED) {
                record()
            } else {
                ActivityCompat.requestPermissions(
                    this, arrayOf(
                        android.Manifest.permission.RECORD_AUDIO,
                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        android.Manifest.permission.READ_EXTERNAL_STORAGE
                    ), REQUEST_RECORD)
            }
        }
        stop.setOnClickListener {
            stop()
        }

        flowing.observe(this) { inFlow ->
            start.isEnabled = !inFlow
            stop.isEnabled = inFlow
        }
        bt_track.setSafeListener {
            stop()
            JumpUtil.overlay(this,AudioTrackActivity::class.java)
        }
        bt_to_play.setSafeListener {
            JumpUtil.overlay(this,PlayActivity::class.java)
        }
    }

    fun go(){
        Handler().postDelayed(100) {
            //JumpUtil.overlay(this,CameraActivity::class.java)
            JumpUtil.overlay(this,RoomActivity::class.java)
        }

    }

    fun record() {
        flowing.value = true
        if (recordStart) {
            toast("already start")
            flowing.value=false
            return
        }
        file = File(file1, "${System.currentTimeMillis()}.pcm").apply {
            if (!exists()) createNewFile()
        }
        fileOutputStream = FileOutputStream(file)
        if (fileOutputStream == null) {
            Log.e("fuck", "file not open")
            flowing.value = false
            return
        }


        audioRecord?.startRecording()
        toast("start recording")
        loopStopped.set(false)

        thread = Thread(runnable).apply {
            start()
        }

        recordStart = true
    }

    var lastFile = ""

    //@Volatile var waitStoppping=false


    @SuppressLint("InlinedApi")
    fun stop() {
        loopStopped.set(true)
        //waitStoppping=true
        Log.e("fucktime1", System.currentTimeMillis().toString())
        thread?.join(1000) //assume that main is waiting for 1s
        Log.e("fucktime2", System.currentTimeMillis().toString())
        //waitStoppping=false
        fileOutputStream?.close()
        //toast(file?.absolutePath ?: return)
        audioRecord?.stop()

        if (file != null && ! lastFile.equals(file!!.name.substringAfterLast("/"))) {
            lastFile = file!!.name
            val fileInputStream = FileInputStream(file)
            jobScope.launch {
                Log.e("isJobMain", "${Looper.myLooper() == Looper.getMainLooper()}")
                val resolver = applicationContext.contentResolver
                val volume =
                    if (Build.VERSION.SDK_INT > 28) MediaStore.VOLUME_EXTERNAL_PRIMARY else MediaStore.VOLUME_EXTERNAL
                val audioCollection = MediaStore.Audio.Media.getContentUri(volume)
                val detail = ContentValues().apply {
                    put(MediaStore.Audio.Media.DISPLAY_NAME, file!!.name)
                    put(MediaStore.Audio.Media.IS_PENDING, 1)
                }
                val songContentUri = resolver.insert(audioCollection, detail)
                resolver.openFileDescriptor(songContentUri ?: return@launch, "w", null).use{ pdf ->
                    FileOutputStream(pdf?.fileDescriptor ?: return@launch).use {
                        val buffer = ByteArray(50)
                        while (true) {
                            val read = fileInputStream.read(buffer)
                            if (read == -1) break
                            it.write(buffer, 0, read)
                        }
                    }
                }
                detail.clear()
                detail.put(MediaStore.Audio.Media.IS_PENDING, 0)
                Log.e("fuckaudio", songContentUri.toString())
                resolver.update(songContentUri, detail, null, null)
            }
        }

        recordStart = false
        flowing.value = false //end a record flow 结束一个录制流程
    }

    override fun onDestroy() {
        super.onDestroy()
        loopStopped.set(true)
        audioRecord?.stop()
        audioRecord?.release()
        job.cancel()
    }

    val runnable = Runnable {
        while (!loopStopped.get()) {
            val byteArray = ByteArray(size)
            val readNum = audioRecord?.read(byteArray, 0, size)
            when (readNum) {
                AudioRecord.ERROR_BAD_VALUE -> toast("fuck" + "bad value")
                AudioRecord.ERROR_INVALID_OPERATION -> toast("fuck" + "bad value")
                AudioRecord.ERROR_DEAD_OBJECT -> toast("fuck" + "dead object")
                AudioRecord.ERROR -> toast("fuck" + "error")
                else -> fileOutputStream!!.write(byteArray)
            }
        }
        Log.e("fucktimestop", System.currentTimeMillis().toString())
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if(requestCode==REQUEST_RECORD  ){
            var res=true
            grantResults.forEach {
                if(it!=PackageManager.PERMISSION_GRANTED)
                    res=false
            }
            if(res){
                if(audioRecord?.state==AudioRecord.STATE_UNINITIALIZED){
                    audioRecord = AudioRecord(source, sampleRate, channel, audioFormat, size)
                }
            }
        }
    }

    fun test(){
        lifecycleScope
    }


}
